//********************************************************************************************************
// Product Name: MapWindow.dll Alpha
// Description:  Low level interfaces that allow separate components to use objects that are defined
//               in completely independant libraries so long as they satisfy the basic interface requirements.
//********************************************************************************************************
// The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); 
// you may not use this file except in compliance with the License. You may obtain a copy of the License at 
// http://www.mozilla.org/MPL/ 
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF 
// ANY KIND, either express or implied. See the License for the specificlanguage governing rights and 
// limitations under the License. 
//
// The Original Code is MapWindow.Interfaces
//
// The Initial Developer of this Original Code is Ted Dunsford. Created in August, 2007.
// 
// Contributor(s): (Open source contributors should list themselves and their modifications here). 
//
//********************************************************************************************************
using System;
using System.Collections.Generic;
using System.Text;
using MapWindow.Data;
using MapWindow.Geometries;
using MapWindow.Main;
using System.Drawing;
namespace MapWindow.Drawing
{
    /// <summary>
    /// A Layer of Polygons with Drawing Characteristics
    /// </summary>
    public class MapPolygonLayer: MapFeatureLayer, IMapPolygonLayer
    {
        #region Variable

        /// <summary>
        /// Controls the drawing aspects
        /// </summary>
        protected IPolygonSymbolizer _PolygonSymbolizer; // Acts as the default for any empty points symbolizers on this layer

        /// <summary>
        /// MapPolygons listed within this layer
        /// </summary>
        protected IEventList<IMapPolygon> _MapPolygons;
       

        #endregion


        /// <summary>
        /// Constructs a new instance of a MapPointLayer from a feature layer, but only if that
        /// feature layer is a Point or Multipoint geometry type
        /// </summary>
        /// <param name="featureLayer">The feature layer containing the data elements of the points in question</param>
        /// <exception cref="System.ArgumentException">The feature layer was not a point feature layer.</exception>
        public MapPolygonLayer(IFeatureLayer featureLayer)
            : base(featureLayer)
        {
           

            // Set defaults
            _PolygonSymbolizer = new PolygonSymbolizer();
            //_PolygonSymbolizer.BorderPen = new Pen(Color.Black);
            //_PolygonSymbolizer.FillColor = Color.Beige;
            //_PolygonSymbolizer.Visible = true;
            _FeatureSymbolizer = _PolygonSymbolizer;
            _MapPolygons = new MapPolygonCollection(this);
            

        }


        /// <summary>
        /// Copies all the point symbol properties from any valid implementation of IPolygonSymbolizer
        /// like a MapPolygon
        /// </summary>
        /// <param name="copyFrom"></param>
        public void Copy(IPolygonSymbolizer copyFrom)
        {
            _PolygonSymbolizer.Copy(copyFrom);
        }
 
        /// <summary>
        /// This simply calls the draw function for each of its specific features, if it has them defined,
        /// or else it calls the default characteristics.
        /// TO DO: Support Symbols based on attributes
        /// </summary>
        /// <param name="g"></param>
        /// <param name="clipRectangle"></param>
        /// <param name="target"></param>
        public override void Draw(System.Drawing.Graphics g, System.Drawing.Rectangle clipRectangle, System.Drawing.Rectangle target)
        {
            _IsBusy = true;
            try
            {
                // We can't draw without a graphics surface
                if (g == null)
                {
                    OnPaintCancelled(new ArgumentNullException("g cannot be null."), g, Rectangle.Empty, Rectangle.Empty);
                    return;
                }

                System.Drawing.Drawing2D.Matrix mat = (System.Drawing.Drawing2D.Matrix)g.Transform.Clone();
                System.Drawing.Drawing2D.Matrix oldMatrix = g.Transform;

                // Obviously don't draw the layer if it isn't supposed to be visible
                if (this.Visible == false)
                {
                    OnPaintCancelled(new OperationCanceledException("Visible was false"), g, clipRectangle, target);
                    return;
                }

                // The OnBeforePainting method can cancel the drawing
                if (OnBeforePainting(g, clipRectangle, target) == true)
                {
                    OnPaintCancelled(new OperationCanceledException("Canceled by OnBeforePainting"), g, clipRectangle, target);
                    return;
                }

                // Usually clipping is done at the MapFrame level, but custom users should be able to draw the layer too
                if (clipRectangle.IsEmpty == false)
                {
                    // use the intersection of any clipping that has already been applied and the specified rectangle
                    g.IntersectClip(clipRectangle);
                }



                // Create a Transform if a target exists, otherwise, just use the existing transform
                if (target.IsEmpty == false)
                {
                    // Before applying any transforms, modify the clip region in the previous transform environment
                    g.IntersectClip(target);

                    // Initialize a new transform that will scale the image to the target
                    mat = new System.Drawing.Drawing2D.Matrix();

                    // While we are in pixel coordinates, translate to the specified Frame
                    mat.Translate((float)target.X, (float)target.Y);

                    // Scaling 
                    // An envelope width or height of 0 will throw an exception.
                    // I think that is better than simply not drawing, since it helps track the problem
                    float xScale = (float)(target.Width / Envelope.Width);
                    float yScale = (float)(target.Height / Envelope.Height);
                    float maxScale = Math.Min(xScale, yScale);

                    // By using the maxScale for both, we will preserve the aspect ratio, but fit the image
                    // completely into the view.  We also invert Y to account for geographic coordinates
                    // generally having Y get larger as you go up.
                    mat.Scale(maxScale, -maxScale);

                    // If a target is specified, we have to throw out the expectation that the draw instruction is
                    // coming from a view frame and instead try to fit the entire layer into the specified frame,
                    // while still preserving the aspect ratio.
                    mat.Translate((float)-Envelope.MinX, -(float)Envelope.MaxY);

                    // I control the transform this way because I can't seem to apply methods to the 
                    // graphics transform directly.
                    g.Transform = mat;

                }

                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
                System.Drawing.Drawing2D.Matrix oldPosition = (System.Drawing.Drawing2D.Matrix)g.Transform.Clone();
                for (int shp = 0; shp < Features.Count; shp++)
                {

                    if (_CancelDrawing == true) // Check this each time to bail as quickly as possible
                    {
                        OnPaintCancelled(new OperationCanceledException("CancelDrawing was true"), g, clipRectangle, target);
                    }

                    mat = (System.Drawing.Drawing2D.Matrix)oldPosition.Clone();
                    //mat.Translate(-this.MapPoints[shp].Position.X, -this.MapPoints[shp].Position.Y); // Translate to the location of the point
                    //mat.Translate((float)this.Features[shp].BasicGeometry.Coordinates[0].X, (float)this.Features[shp].BasicGeometry.Coordinates[0].Y);
                    // Flip back to the normal orientation after translation, but before drawing images
                   // mat.Multiply(new System.Drawing.Drawing2D.Matrix(1f, 0f, 0f, -1f, 0f, 0f));

                    g.Transform = mat;

                    // g now already contains all the necessary information from target or clipRectangle
                    _MapPolygons[shp].Draw(g, Rectangle.Empty, Rectangle.Empty);



                }

                g.Transform = oldMatrix;
            }
            catch (Exception ex)
            {

                // Only throw the event if this method was called directly, otherwise, pass up the exception
                OnPaintCancelled(ex, g, clipRectangle, target);

            }

            OnAfterPainting(g, clipRectangle, target);
        }


        #region Properties

        /// <summary>
        /// Gets or sets the polygon symbolizer to use as the default symbolizer for this layer.
        /// </summary>
        public IPolygonSymbolizer PolygonSymbolizer
        {
            get { return _PolygonSymbolizer; }
            set { _PolygonSymbolizer = value; }
        }

        #endregion
    }
}
